<?php
/**
 * Package release management within the SimpleChannelServer.
 *
 * @category Developer
 * @package  PEAR2_SimpleChannelServer
 * @author   Greg Beaver <cellog@php.net>
 * @license  New BSD?
 * @link     http://svn.pear.php.net/wsvn/PEARSVN/sandbox/SimpleChannelServer/
 */
class PEAR2_SimpleChannelServer_REST_Release extends
      PEAR2_SimpleChannelServer_REST_Manager
{
    /**
     * Save a package release's REST information
     *
     * @param PEAR2_Pyrus_Package $new      new package to be released
     * @param string              $releaser handle of the maintainer who released this package
     *
     * @return void
     */
    function save(PEAR2_Pyrus_Package $new, $releaser)
    {
        $this->saveInfo($new, $releaser);
        $this->saveInfo2($new, $releaser);
        $this->saveMaintainers($new);
        $this->saveAll($new);
        $this->saveStability($new);
        $this->savePackageDeps($new);
        $this->savePackageXml($new);
    }

    /**
     * Delete a release from REST information
     *
     * @param PEAR2_Pyrus_Package $new Package to be erased
     *
     * @return void
     */
    function erase(PEAR2_Pyrus_Package $new)
    {
        $this->saveAll($new, true);
        $xml = $this->saveAll2($new, true);
        if (!count($xml['a']['r'])) return;
        // reconstruct stability stuff
        foreach ($xml['a']['r'] as $release) {
            if (!isset($latest)) {
                $latest = $release['v'];
            }
            if ($release['s'] == 'stable' && !isset($stable)) {
                $stable = $release['v'];
            }
            if ($release['s'] == 'beta' && !isset($beta)) {
                $beta = $release['v'];
            }
            if ($release['s'] == 'alpha' && !isset($alpha)) {
                $alpha = $release['v'];
            }
        }
        $this->saveReleaseREST(strtolower($new->name) . '/latest.txt', $latest, false);
        if (isset($stable)) {
            $this->saveReleaseREST(strtolower($new->name) . '/stable.txt', $stable, false);
        }
        if (isset($beta)) {
            $this->saveReleaseREST(strtolower($new->name) . '/beta.txt', $beta, false);
        }
        if (isset($alpha)) {
            $this->saveReleaseREST(strtolower($new->name) . '/alpha.txt', $alpha, false);
        }
    }

    /**
     * save rest.release release REST
     *
     * @param PEAR2_Pyrus_Package $new      package to save info for
     * @param string              $releaser releasing maintainer's handle
     *
     * @return void
     */
    function saveInfo(PEAR2_Pyrus_Package $new, $releaser)
    {
        $xml            = $this->_getProlog('r', 'release');
        $xml['r']['p']  = array(
                'attribs' => array(
                'xlink:href' =>
                $this->getPackageRESTLink(strtolower($new->name))
            ),
            '_content' => $new->name);
        $xml['r']['c']  = $this->channel;
        $category       = PEAR2_SimpleChannelServer_Categories::getPackageCategory($new->name);
        $xml['r']['v']  = $new->version['release'];
        $xml['r']['st'] = $new->stability['release'];
        $license        = $new->license;
        $xml['r']['l']  = is_array($license) ? $license['_content'] : $license;
        $xml['r']['m']  = $releaser;
        $xml['r']['s']  = $new->summary;
        $xml['r']['d']  = $new->description;
        $xml['r']['da'] = $new->date;
        $xml['r']['n']  = $new->notes;
        $xml['r']['f']  = filesize($new->archivefile);
        $xml['r']['g']  = 'http://' . $this->channel . '/get/' . $new->name .
            '-' . $new->version['release'];
        $xml['r']['x']  = array('attribs' => array(
            'xlink:href' => 'package' . $new->version['release'] . '.xml'
        ));
        $this->saveReleaseREST(strtolower($new->name) . '/' .
            $new->version['release'] . '.xml', $xml);
    }

    /**
     * save rest.release2 release REST
     *
     * @param PEAR2_Pyrus_Package $new      package to be saved
     * @param string              $releaser releasing maintainer's handle
     *
     * @return void
     */
    function saveInfo2(PEAR2_Pyrus_Package $new, $releaser)
    {
        $xml            = $this->_getProlog('r', 'release2');
        $xml['r']       = array(
                'attribs' => array(
                'xlink:href' =>
                $this->getPackageRESTLink(strtolower($new->name))
            ),
            '_content' => $new->name);
        $xml['r']['c']  = $this->channel;
        $category       = PEAR2_SimpleChannelServer_Categories::getPackageCategory($new->name);
        $xml['r']['v']  = $new->version['release'];
        $xml['r']['a']  = $new->version['api'];
        $xml['r']['mp'] = $new->dependencies->required->php;
        $xml['r']['st'] = $new->stability['release'];
        $license        = $new->license;
        $xml['r']['l']  = is_array($license) ? $license['_content'] : $license;
        $xml['r']['m']  = $releaser;
        $xml['r']['s']  = $new->summary;
        $xml['r']['d']  = $new->description;
        $xml['r']['da'] = $new->date;
        $xml['r']['n']  = $new->notes;
        $xml['r']['f']  = filesize($new->archivefile);
        $xml['r']['g']  = 'http://' . $this->channel . '/get/' . $new->name .
            '-' . $new->version['release'];
        $xml['r']['x']  = array('attribs' => array(
            'xlink:href' => 'package' . $new->version['release'] . '.xml'
        ));
        $this->saveReleaseREST(strtolower($new->name) . '/v2' .
            $new->version['release'] . '.xml', $xml);
    }

    /**
     * Save a release's package.xml contents
     *
     * @param PEAR2_Pyrus_Package $new package to be saved
     *
     * @return void
     */
    function savePackageXml(PEAR2_Pyrus_Package $new)
    {
        $this->saveReleaseREST(strtolower($new->name) . '/package' .
            $new->version['release'] . '.xml', file_get_contents($new->packagefile),
            false);
    }

    /**
     * Save a serialized representation of a package's dependencies
     *
     * @param PEAR2_Pyrus_Package $new package to be saved
     *
     * @return void
     */
    function savePackageDeps(PEAR2_Pyrus_Package $new)
    {
        $this->saveReleaseREST(strtolower($new->name) . '/deps.' .
            $new->version['release'] . '.txt', serialize($new->rawdeps),
            false);
    }

    /**
     * Save package maintainers information for this release
     *
     * @param PEAR2_Pyrus_Package $new package to be saved
     *
     * @return void
     */
    function saveMaintainers(PEAR2_Pyrus_Package $new)
    {
        $m  = $this->_getProlog('m', 'packagemaintainers');
        $m2 = $this->_getProlog('m', 'packagemaintainers2');

        $m['m']['p'] = $m2['m']['p'] = $new->name;
        $m['m']['c'] = $m2['m']['c'] = $this->chan;
        $m['m']['m'] = $m2['m']['m'] = array();

        foreach ($new->allmaintainers as $role => $maintainers) {
            if (!$maintainers) continue;
            foreach ($maintainers as $dev) {
                $m['m']['m'][]  = array('h' => $dev['user'], 'a' => $dev['active']);
                $m2['m']['m'][] = array(
                    'h' => $dev['user'],
                    'a' => $dev['active'],
                    'r' => $role
                );
            }
        }
        $this->saveReleaseREST(strtolower($new->name) . '/maintainers.xml',
            $m);
        $this->saveReleaseREST(strtolower($new->name) . '/maintainers2.xml',
            $m2);
    }

    /**
     * save REST information for all releases of this package
     *
     * @param PEAR2_Pyrus_Package $new   package to save all release info for
     * @param bool                $erase if true, the release represented by the
     *                                   version of $new will be removed.
     *
     * @return void
     */
    function saveAll(PEAR2_Pyrus_Package $new, $erase = false)
    {
        $reader = new PEAR2_Pyrus_XMLParser;
        $path   = $this->getRESTPath('r', strtolower($new->name) .
            DIRECTORY_SEPARATOR . 'allreleases.xml');
        if (file_exists($path)) {
            $xml = $reader->parse($path);
            if (!isset($xml['a']['r'][0])) {
                $xml['a']['r'] = array($xml['a']['r']);
            }
        } else {
            $xml           = $this->_getProlog('a', 'allreleases');
            $xml['a']['p'] = $new->name;
            $xml['a']['c'] = $this->chan;
            $xml['a']['r'] = array();
        }
        if ($erase) {
            foreach ($xml['a']['r'] as $i => $release) {
                if ($release['v'] === $new->version['release']) {
                    unset($xml['a']['r'][$i]);
                    $xml['a']['r'] = array_values($xml['a']['r']);
                    break;
                }
            }
            if (!count($xml['a']['r'])) {
                // no releases, erase all traces
                foreach (new DirectoryIterator($this->getRESTPath('r',
                         strtolower($new->name))) as $name => $info) {
                    if ($info->isDot()) continue;
                    unlink($name);
                }
            }
        } else {
            $info = array(
                'v' => $new->version['release'],
                's' => $new->stability['release'],
            );
            if (count($new->compatible)) {
                $info['co'] = array();
                foreach ($new->compatible as $package=>$cinfo) {
                    if (strpos($package, '/')) {
                        $c = substr($package, 0, strpos($package, '/'));
                        $package = str_replace(array($c, '/'), '', $package);
                    } else {
                        $c = 'pear.php.net';
                    }
                    unset($cinfo['channel']);
                    unset($cinfo['package']);
                    if (isset($cinfo['exclude'])) {
                        $info['x'] = $cinfo['exclude'];
                        unset($cinfo['exclude']);
                    }

                    $info['co'][] = array_merge(array('c' => $c, 'p' => $package), $cinfo);
                }
            }
            array_unshift($xml['a']['r'], $info);
        }
        $this->saveReleaseREST(strtolower($new->name) . '/allreleases.xml', $xml);
    }

    /**
     * save REST information for all releases (version 2) of this package
     *
     * @param PEAR2_Pyrus_Package $new   package to save all releases for
     * @param bool                $erase if true, the release represented by the
     *                                   version of $new will be removed.
     *
     * @return void
     */
    function saveAll2(PEAR2_Pyrus_Package $new, $erase = false)
    {
        $reader = new PEAR2_Pyrus_XMLParser;
        $path   = $this->getRESTPath('r', strtolower($new->name) .
            DIRECTORY_SEPARATOR . 'allreleases2.xml');
        if (file_exists($path)) {
            $xml = $reader->parse($path);
            if (!isset($xml['a']['r'][0])) {
                $xml['a']['r'] = array($xml['a']['r']);
            }
        } else {
            $xml           = $this->_getProlog('a', 'allreleases2');
            $xml['a']['p'] = $new->name;
            $xml['a']['c'] = $this->chan;
            $xml['a']['r'] = array();
        }
        if ($erase) {
            foreach ($xml['a']['r'] as $i => $release) {
                if ($release['v'] === $new->version['release']) {
                    unset($xml['a']['r'][$i]);
                    $xml['a']['r'] = array_values($xml['a']['r']);
                    break;
                }
            }
        } else {
            $info = array(
                'v' => $new->version['release'],
                's' => $new->stability['release'],
                'm' => $new->dependencies->required['php']->min,
            );
            if (count($new->compatible)) {
                $info['co'] = array();
                foreach ($new->compatible as $info) {
                    $c = $info['channel'];
                    $p = $info['package'];
                    unset($info['channel']);
                    unset($info['package']);
                    if (isset($info['exclude'])) {
                        $info['x'] = $info['exclude'];
                        unset($info['exclude']);
                    }
                    $info['co'][] = array_merge(array('c' => $c, 'p' => $p), $info);
                }
            }
            array_unshift($xml['a']['r'], $info);
        }
        $this->saveReleaseREST(strtolower($new->name) . '/allreleases.xml', $xml);
        return $xml;
    }

    /**
     * save REST stability version info in .txt files
     *
     * @param PEAR2_Pyrus_Package $new package to save stability for
     *
     * @return void
     */
    function saveStability(PEAR2_Pyrus_Package $new)
    {
        $this->saveReleaseREST(strtolower($new->name) . '/latest.txt',
            $new->version['release'], false);
        $this->saveReleaseREST(strtolower($new->name) . '/' . $new->state . '.txt',
            $new->version['release'], false);
    }
}
